<template>
  <div class="chat">
    <h1> 科大讯飞星火认知模型 </h1>
    <div class="content" ref="content">
      <div v-for="(item, i) in state.recordContent" :key="i">
        <div class="contentGuest" v-if="item.guest"
          ><img src="../../assets/AI.png" alt="" />
          <div class="contentCard" v-html="mdParseToHtml(item.content)" v-hljs> </div>
          <div class="guestTime">{{ item.time }}</div>
        </div>
        <div class="contentMaster" v-else>
          <div class="contentCard contentCard2">
            <p> {{ item.content }}</p>
          </div>
          <img src="../../assets/avatar.jpg" alt="" />
          <div class="masterTime">{{ item.time }}</div>
        </div>
      </div>
    </div>
    <div class="bottomBar">
      <el-input
        :disabled="state.notWrite"
        v-model="state.sendMessage"
        @keyup.enter="sendMsg"
      ></el-input>
      <el-button @click="sendMsg" type="primary" style="height: 50px; margin-left: 10px">
        <component class="icon-send" size="20" :is="'icon-send'" /> 发送</el-button
      >
    </div>
  </div>
</template>

<script>
  export default {
    name: 'Vue3AdminElementTemplateMasterRemoveMockXFYUN',
  };
</script>

<script setup>
  import ClipboardJS from 'clipboard';
  import MarkdownIt from 'markdown-it';
  import * as base64 from 'base-64';
  import CryptoJs from 'crypto-js';
  import { getNormalTime } from '../../utils/time';
  import { reactive, ref, nextTick } from 'vue';
  import { ElMessage } from 'element-plus';
  const content = ref(null);

  const mdParseToHtml = (mdValue) => {
    const md = new MarkdownIt();
    console.log(md);
    return md.render(mdValue);
  };
  const scrollToBottom = () => {
    content.value.scrollTop = content.value.scrollHeight;
  };
  const loadUrl = () => {
    let url = 'ws://spark-api.xf-yun.com/v1.1/chat';
    let host = 'spark-api.xf-yun.com';
    let apiKeyName = 'api_key';
    let date = new Date().toGMTString();
    let algorithm = 'hmac-sha256';
    let headers = 'host date request-line';
    let signatureOrigin = `host: ${host}\ndate: ${date}\nGET /v1.1/chat HTTP/1.1`;
    let signatureSha = CryptoJs.HmacSHA256(signatureOrigin, state.APISecret);
    let signature = CryptoJs.enc.Base64.stringify(signatureSha);
    let authorizationOrigin = `${apiKeyName}="${state.APIKey}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
    let authorization = base64.encode(authorizationOrigin);
    // 将空格编码
    url = `${url}?authorization=${authorization}&date=${encodeURI(date)}&host=${host}`;
    return url;
  };
  const state = reactive({
    notWrite: false,
    content: '',
    value: '123',
    knowledge: '',
    appId: '3322360f',
    APIKey: '69d8173e53f980aaed4ebfcf40ca22ec',
    APISecret: 'NWJmZmJmOTNlY2ZhYTk1ODdjNGYzZmI4',
    recordContent: [],
    sendMessage: '',
    url: '',
  });

  const sendMsg = () => {
    state.url = loadUrl();
    if (state.sendMessage != '') {
      state.recordContent.push({
        guest: false,
        content: state.sendMessage,
        time: getNormalTime(),
      });
      let params = {
        header: {
          app_id: state.appId,
          uid: 'hannz',
        },
        parameter: {
          chat: {
            domain: 'general',
            temperature: 0.5,
            max_tokens: 1024,
          },
        },
        payload: {
          message: {
            // 如果想获取结合上下文的回答，需要开发者每次将历史问答信息一起传给服务端，如下示例
            // 注意：text里面的所有content内容加一起的tokens需要控制在8192以内，开发者如有较长对话需求，需要适当裁剪历史信息
            text: [
              { role: 'user', content: state.sendMessage }, //# 最新的一条问题，如无需上下文，可只传最新一条问题
            ],
          },
        },
      };
      // params.payload.message.text.unshift()
      let arr = state.recordContent.map((v) => {
        if (v.guest) {
          return {
            role: 'assistant',
            content: v.content,
          };
        } else {
          return {
            role: 'user',
            content: v.content,
          };
        }
      });
      if (arr.length > 10) {
        arr = arr.slice(arr.length - 10);
      }
      params.payload.message.text.unshift(...arr);

      const ws = new WebSocket(state.url);
      state.recordContent.push({
        guest: true,
        content: state.content,
        time: getNormalTime(),
      });
      ws.addEventListener('open', (event) => {
        console.log('发送消息');
        ws.send(JSON.stringify(params));
      });
      ws.addEventListener('message', (event) => {
        state.notWrite = true;
        let data = JSON.parse(event.data);
        state.content += data.payload.choices.text[0].content;
        state.recordContent[state.recordContent.length - 1].content +=
          data.payload.choices.text[0].content;
        if (data.header.code !== 0) {
          console.log('出错了', data.header.code, ':', data.header.message);
          // 出错了"手动关闭连接"
          ws.close();
        }
        if (data.header.code === 0) {
          // 对话已经完成
          if (data.payload.choices.text && data.header.status === 2) {
            // requestObj.sparkResult += data.payload.choices.text[0].content;
            setTimeout(() => {
              // "对话完成，手动关闭连接"
              ws.close();
            }, 1000);
          }
        }
        nextTick(() => {
          scrollToBottom();
        });
        console.log('收到消息：', data);
      });
      ws.addEventListener('close', (event) => {
        if (event.wasClean) {
          console.log('WebSocket连接已关闭');
        } else {
          console.error('WebSocket连接异常断开');
          state.url = loadUrl();
          sendMsg();
        }
        state.content = '';
        nextTick(() => {
          scrollToBottom();
        });
        const pre = document.querySelectorAll('pre');
        pre.forEach((block) => {
          const copyBtn = document.createElement('button');
          copyBtn.className = 'copyBtn';
          copyBtn.textContent = '复制';
          const code = block.querySelector('code');
          copyBtn.addEventListener('click', () => {
            const codeToCopy = code.textContent;
            // 在这里执行复制代码的逻辑
            console.log(codeToCopy);
            const tempTextarea = document.createElement('textarea');
            tempTextarea.textContent = codeToCopy;
            block.appendChild(tempTextarea);
            tempTextarea.select();
            document.execCommand('copy');
            block.removeChild(tempTextarea);
            ElMessage.success('代码已成功复制到剪贴板');
          });
          let codeClass = code.classList[0];
          let str = codeClass.replace('language-', '');
          const codeType = document.createElement('div');
          codeType.className = 'codeType';
          codeType.textContent = str;
          block.prepend(codeType);
          block.prepend(copyBtn);
        });
        state.notWrite = false;
      });
      ws.addEventListener('error', (error) => {
        console.error('WebSocket连接错误：', error);
      });

      state.sendMessage = '';
    } else {
      ElMessage.warning('输入为空！');
    }
  };
</script>

<style lang="scss" scoped>
  ::v-deep pre {
    code {
      padding: 25px;
      padding-top: 30px;
    }
    .codeType {
      position: absolute;
      top: 55px;
      left: 80px;
      font-size: 12px;
      color: #409eff;
    }
    .copyBtn {
      position: absolute;
      top: 55px;
      left: 40px;
      font-size: 12px;
      color: #409eff;
      cursor: pointer;
      border: none;
      background-color: transparent;
    }

    .codeBlock {
      position: relative;
      padding-top: 30px;
    }

    .copyBtn:hover::before {
      color: #fff;
      background-color: #409eff;
    }
  }
  .contentCard2 {
    padding: 5px;
    padding-top: 10px;
    padding-bottom: 10px;
  }
  .contentCard {
    background-color: antiquewhite;
    border-radius: 5px;
    padding: 5px;
  }
  /*   ::v-deep pre {
      background-color: black;
      color: white;
      padding: 5px;
      position: relative;
    }
    ::v-deep code::after {
      content: 'copy';
      color: #ccc;
      position: absolute;
      right: 5px;
      top: 2px;
      cursor: pointer;
      z-index: 1;
    } */

  .chat {
    padding: $base-main-padding;
    background-color: $base-color-white;
  }
  .content {
    height: 600px;
    overflow-x: scroll;
    overflow-y: auto;
    border: 1px solid;
    padding: 20px;
    word-break: break-all;
    .contentGuest {
      display: flex;
      align-items: center;
      margin-bottom: 50px;
      min-height: 20px;
      position: relative;
      overflow-wrap: break-all;
      word-wrap: break-all;
      pre {
        overflow-wrap: break-all;
        word-wrap: break-all;
      }
      img {
        border-radius: 50%;
        width: 20px;
        margin-right: 10px;
      }
      .guestTime {
        font-size: 10px;
        position: absolute;
        bottom: -20px;
        color: hotpink;
      }
    }

    .contentMaster {
      display: flex;
      align-items: center;
      margin-bottom: 50px;
      justify-content: flex-end;
      min-height: 20px;
      position: relative;
      word-wrap: break-all;
      overflow-wrap: break-all;
      pre {
        overflow-wrap: break-all;
        word-wrap: break-all;
      }
      img {
        border-radius: 50%;
        width: 20px;
        margin-left: 10px;
      }
      .masterTime {
        font-size: 10px;
        position: absolute;
        bottom: -20px;
        color: hotpink;
      }
    }
  }

  .bottomBar {
    margin-top: 10px;
    height: 50px;
    border: 1px solid;
    display: flex;
    padding: 10px;
  }
</style>
